#[macro_use]
extern crate rbatis;

#[macro_use]
extern crate lazy_static;

mod dbs;
mod features;
mod gql;
mod updater;
mod util;

use crate::gql::schema::{build_schema, graphiql, graphql};
use actix_cors::Cors;
use actix_web::{guard, http, web, App, HttpServer};
// use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};
use std::path::PathBuf;
use std::sync::Mutex;

use serde_json::Value as JsonValue;
use tauri::{
  plugin::{Plugin, Result as PluginResult},
  AppHandle, Runtime,
};

pub struct Localhost {
  port: u16,
  database_url: String,
  sqllog_path: String,
}

lazy_static! {
  static ref GLOBAL_PORT: Mutex<u16> = Mutex::new(8180);
  static ref DATABASE_URL: Mutex<String> = Mutex::new("./data/sql/ebmstore".to_string());
  static ref SQLLOG_PATH: Mutex<String> = Mutex::new("./data/logs/".to_string());
}

pub fn get_port() -> u16 {
  let data = GLOBAL_PORT.lock().unwrap();
  *data
}

impl Localhost {
  pub fn new(port: u16, database_url1: &str, sqllog_path1: &str) -> Self {
    let mut port_ = GLOBAL_PORT.lock().expect("Could not lock mutex");
    *port_ = port;

    let mut database_url_ = DATABASE_URL.lock().expect("Could not lock mutex");
    *database_url_ = database_url1.to_string();

    let mut sqllog_path_ = SQLLOG_PATH.lock().expect("Could not lock mutex");
    *sqllog_path_ = sqllog_path1.to_string();

    Self {
      port: port,
      database_url: database_url1.to_string(),
      sqllog_path: sqllog_path1.to_string(),
    }
  }
}

impl<R: Runtime> Plugin<R> for Localhost {
  fn name(&self) -> &'static str {
    "localhost"
  }

  fn initialize(&mut self, app: &AppHandle<R>, _: JsonValue) -> PluginResult<()> {
    let port = self.port;
    let database_url = &self.database_url;
    let sqllog_path = &self.sqllog_path;

    let mut res_dir: String = String::from("./");
    let path_buf: Option<PathBuf> = app.path_resolver().resource_dir();
    match path_buf {
      Some(v) => {
        let s = v.to_str();
        match s {
          Some(t) => {
            res_dir = t.to_string();
          }
          _ => {}
        }
      }
      _ => {}
    }

    let mut data_dir: String = String::from("./");
    let path_buf: Option<PathBuf> = tauri::api::path::home_dir();
    match path_buf {
      Some(v) => {
        let s = v.to_str();
        match s {
          Some(t) => {
            data_dir = t.to_string();
          }
          _ => {}
        }
      }
      _ => {}
    }

    common::init::init(&data_dir, &res_dir, &database_url, &sqllog_path);
    db_diesel::migrate(&common::init::DATABASE_URL.read().unwrap());

    tauri::async_runtime::spawn(async move {
      let _ = actix_web::rt::System::new("gql")
        .block_on(async move { self::start_gql_server(&data_dir,port).await });
      println!("come to end of tauri::async_runtime::spawn ");
    });

    Ok(())
  }
}

async fn start_gql_server(data_dir:&str, port: u16) {
  let schema: gql::schema::ActixSchema =
    build_schema(data_dir,&common::init::DATABASE_URL.read().unwrap()).await;

  println!("GraphQL UI: http://localhost:{}/v1", port);
  // println!("GraphQL UI: https://localhost:{}/v1", port);

  /*
  // load ssl keys
  // to create a self-signed temporary cert for testing:
  // `openssl req -x509 -newkey rsa:4096 -nodes -keyout key.pem -out cert.pem -days 365 -subj '/CN=localhost'`
  let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();

  let key_file: String = common::init::KEY_PATH.read().unwrap().to_owned();
  let cert_file: String = common::init::CERT_PATH.read().unwrap().to_owned();

  println!("\nkey_file={}\n cert_file={}\n", key_file, cert_file);
  builder
    .set_private_key_file(&key_file, SslFiletype::PEM)
    .unwrap();
  builder
    .set_certificate_chain_file(&cert_file)
    .unwrap();
   HttpServer::new(|| App::new().service(index))
  .bind_openssl("localhost:8080", builder)?
  .run()
  .await*/

  let result = HttpServer::new(move || {
    let cors = Cors::default()
      .allow_any_origin()
      //.allowed_origin("https://www.rust-lang.org/")
      //.allowed_origin_fn(|origin, _req_head| {
      //    origin.as_bytes().ends_with(b".rust-lang.org")
      //})
      .allowed_methods(vec!["GET", "POST"])
      .allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT])
      .allowed_header(http::header::CONTENT_TYPE)
      .max_age(3600);

    App::new()
      .wrap(cors)
      .data(schema.clone())
      .service(web::resource("v1").guard(guard::Post()).to(graphql))
      .service(web::resource("v1i").guard(guard::Get()).to(graphiql))
      .service(
        web::resource("/update/{target}/{current_version}")
          .guard(guard::Get())
          .to(updater::update),
      )
  })
  // .bind_openssl(format!("localhost:{}", port), builder);
  .bind(format!("{}:{}", "localhost", port));

  if result.is_ok() {
    let _ = result.unwrap().run().await;
  }

  println!("ok");
}
